package service

import (
	"errors"
	"fmt"
	"wms/internal/model"
	"wms/internal/model/request"
	"wms/pkg/global"
	"wms/pkg/logger"

	"go.uber.org/zap"
	"gorm.io/gorm"
)

// WarehouseService 仓库服务
type WarehouseService struct{}

// NewWarehouseService 创建仓库服务
func NewWarehouseService() *WarehouseService {
	return &WarehouseService{}
}

// CreateWarehouse 创建仓库
func (s *WarehouseService) CreateWarehouse(req *request.WarehouseCreateRequest, userID uint) error {
	var count int64
	if err := global.DB.Model(&model.Warehouse{}).Where("code = ?", req.Code).Count(&count).Error; err != nil {
		return err
	}
	if count > 0 {
		return errors.New("仓库编码已存在")
	}

	warehouse := &model.Warehouse{
		Code:           req.Code,
		Name:           req.Name,
		Type:           req.Type,
		Area:           req.Area,
		Capacity:       req.Capacity,
		MinTemperature: req.MinTemperature,
		MaxTemperature: req.MaxTemperature,
		MinHumidity:    req.MinHumidity,
		MaxHumidity:    req.MaxHumidity,
		Remark:         req.Remark,
		Status:         1, // 默认启用
	}

	err := global.DB.Create(warehouse).Error
	if err == nil {
		// 记录业务操作日志
		details := fmt.Sprintf("创建仓库 - 编码:%s, 名称:%s, 类型:%s",
			warehouse.Code, warehouse.Name, warehouse.Type)
		logger.LogOperation("创建仓库", userID, details)
	}
	return err
}

// UpdateWarehouse 更新仓库
func (s *WarehouseService) UpdateWarehouse(req *request.WarehouseUpdateRequest, userID uint) error {
	var warehouse model.Warehouse
	if err := global.DB.First(&warehouse, req.ID).Error; err != nil {
		return errors.New("仓库不存在")
	}

	// 保存旧值用于记录日志
	oldName := warehouse.Name
	oldType := warehouse.Type
	oldStatus := warehouse.Status

	warehouse.Name = req.Name
	warehouse.Type = req.Type
	warehouse.Area = req.Area
	warehouse.Capacity = req.Capacity
	warehouse.MinTemperature = req.MinTemperature
	warehouse.MaxTemperature = req.MaxTemperature
	warehouse.MinHumidity = req.MinHumidity
	warehouse.MaxHumidity = req.MaxHumidity
	warehouse.Remark = req.Remark
	warehouse.Status = req.Status

	err := global.DB.Save(&warehouse).Error
	if err == nil {
		// 记录业务操作日志
		details := fmt.Sprintf("更新仓库 - ID:%d, 名称:%s→%s, 类型:%s→%s, 状态:%d→%d",
			req.ID, oldName, req.Name, oldType, req.Type, oldStatus, req.Status)
		logger.LogOperation("更新仓库", userID, details)
	}
	return err
}

// DeleteWarehouse 删除仓库
func (s *WarehouseService) DeleteWarehouse(id uint, userID uint) error {
	var warehouse model.Warehouse
	if err := global.DB.First(&warehouse, id).Error; err != nil {
		return err
	}

	// 检查是否有关联的货位
	var locationCount int64
	if err := global.DB.Model(&model.Location{}).Where("warehouse_id = ?", id).Count(&locationCount).Error; err != nil {
		return err
	}
	if locationCount > 0 {
		return errors.New("仓库下存在货位，无法删除")
	}

	// 检查是否有关联的库存
	var inventoryCount int64
	if err := global.DB.Model(&model.Inventory{}).Where("warehouse_id = ?", id).Count(&inventoryCount).Error; err != nil {
		return err
	}
	if inventoryCount > 0 {
		return errors.New("仓库下存在库存，无法删除")
	}

	err := global.DB.Delete(&warehouse).Error
	if err == nil {
		// 记录业务操作日志
		details := fmt.Sprintf("删除仓库 - ID:%d, 编码:%s, 名称:%s",
			id, warehouse.Code, warehouse.Name)
		logger.LogOperation("删除仓库", userID, details)
	}
	return err
}

// GetWarehouseByID 获取仓库详情
func (s *WarehouseService) GetWarehouseByID(id uint) (*model.Warehouse, error) {
	var warehouse model.Warehouse
	if err := global.DB.First(&warehouse, id).Error; err != nil {
		return nil, err
	}
	return &warehouse, nil
}

// ListWarehouses 获取仓库列表
func (s *WarehouseService) ListWarehouses(req *request.WarehouseListRequest) ([]*model.Warehouse, int64, error) {
	var warehouses []*model.Warehouse
	var total int64

	// --- Ensure default pagination if missing ---
	if req.Page <= 0 {
		req.Page = 1
	}
	if req.PageSize <= 0 {
		req.PageSize = 10 // Default page size
	}

	// --- Create the base query with filters ---
	query := global.DB.Model(&model.Warehouse{})
	if req.Keyword != "" {
		query = query.Where("code LIKE ? OR name LIKE ?", "%"+req.Keyword+"%", "%"+req.Keyword+"%")
	}
	if req.Type != "" {
		query = query.Where("type = ?", req.Type)
	}
	if req.Status != nil {
		query = query.Where("status = ?", *req.Status)
	}

	// --- Count total using the filtered query ---
	// Create a copy for count
	countQuery := query
	if err := countQuery.Count(&total).Error; err != nil {
		logger.Error("Failed to count warehouses", zap.Error(err))
		return nil, 0, err
	}

	// If count is 0, no need to proceed further
	if total == 0 {
		logger.Info("No warehouses found matching criteria", zap.Any("request", req))
		return warehouses, 0, nil // Return empty slice and 0 total
	}

	// --- Fetch the list using the filtered query + pagination/ordering ---
	offset := (req.Page - 1) * req.PageSize
	limit := req.PageSize
	logger.Debug("Fetching warehouses with pagination", zap.Int("offset", offset), zap.Int("limit", limit)) // Log offset/limit

	// Use the original 'query' object, add pagination/order
	result := query.Offset(offset).Limit(limit).Order("created_at desc").Find(&warehouses) // Capture result

	// --- Log detailed find results ---
	if result.Error != nil {
		logger.Error("Failed to find warehouses", zap.Error(result.Error), zap.Any("request", req))
		// Log SQL for debugging if error occurs
		sqlFind := query.ToSQL(func(tx *gorm.DB) *gorm.DB {
			return tx.Offset(offset).Limit(limit).Order("created_at desc").Find(&model.Warehouse{})
		})
		logger.Debug("Failed Find SQL", zap.String("sql", sqlFind))
		return nil, 0, result.Error
	}
	logger.Debug("Warehouse find operation completed", zap.Int64("rowsAffected", result.RowsAffected), zap.Int("listLength", len(warehouses))) // Log rows affected and result length

	// Check if the list is empty despite finding rows (very unusual, might indicate mapping issues)
	if result.RowsAffected > 0 && len(warehouses) == 0 {
		logger.Warn("GORM Find reported rows affected, but result slice is empty. Potential mapping issue?", zap.Any("request", req))
	}

	return warehouses, total, nil
}

// CreateLocation 创建货位
func (s *WarehouseService) CreateLocation(req *request.LocationCreateRequest, userID uint) error {
	// 检查仓库是否存在
	var warehouse model.Warehouse
	if err := global.DB.First(&warehouse, req.WarehouseID).Error; err != nil {
		return errors.New("仓库不存在")
	}

	// 检查货位编码是否重复
	var count int64
	if err := global.DB.Model(&model.Location{}).Where("code = ?", req.Code).Count(&count).Error; err != nil {
		return err
	}
	if count > 0 {
		return errors.New("货位编码已存在")
	}

	location := &model.Location{
		WarehouseID: req.WarehouseID,
		Code:        req.Code,
		Name:        req.Name, // Use request name, fallback if needed
		Type:        req.Type,
		Area:        req.Area,
		Shelf:       req.Shelf,
		Level:       req.Level,
		Capacity:    req.Capacity,
		Remark:      req.Remark,
		Status:      1, // 默认启用
	}
	// 如果请求中没有 Name，则使用 Code 作为 Name
	if location.Name == "" {
		location.Name = req.Code
	}

	err := global.DB.Create(location).Error
	if err == nil {
		// 记录业务操作日志
		details := fmt.Sprintf("创建货位 - 仓库ID:%d, 编码:%s, 名称:%s",
			req.WarehouseID, location.Code, location.Name)
		logger.LogOperation("创建货位", userID, details)
	}
	return err
}

// UpdateLocation 更新货位
func (s *WarehouseService) UpdateLocation(req *request.LocationUpdateRequest, userID uint) error {
	var location model.Location
	if err := global.DB.First(&location, req.ID).Error; err != nil {
		return errors.New("货位不存在")
	}

	// 保存旧值用于记录日志
	oldCode := location.Code
	oldName := location.Name

	// location.Code = req.Code  // 注意：LocationUpdateRequest中不包含Code字段，保持原有的Code不变
	location.Name = req.Name
	location.Type = req.Type
	location.Area = req.Area
	location.Shelf = req.Shelf
	location.Level = req.Level
	location.Capacity = req.Capacity
	location.Remark = req.Remark
	location.Status = req.Status

	err := global.DB.Save(&location).Error
	if err == nil {
		// 记录业务操作日志
		details := fmt.Sprintf("更新货位 - ID:%d, 编码:%s, 名称:%s→%s",
			req.ID, oldCode, oldName, req.Name)
		logger.LogOperation("更新货位", userID, details)
	}
	return err
}

// DeleteLocation 删除货位
func (s *WarehouseService) DeleteLocation(id uint, userID uint) error {
	var location model.Location
	if err := global.DB.First(&location, id).Error; err != nil {
		return errors.New("货位不存在")
	}

	// 检查是否有关联的库存
	var inventoryCount int64
	if err := global.DB.Model(&model.Inventory{}).Where("location_id = ?", id).Count(&inventoryCount).Error; err != nil {
		return err
	}
	if inventoryCount > 0 {
		return errors.New("货位下存在库存，无法删除")
	}

	err := global.DB.Delete(&location).Error
	if err == nil {
		// 记录业务操作日志
		details := fmt.Sprintf("删除货位 - ID:%d, 编码:%s, 名称:%s, 仓库ID:%d",
			id, location.Code, location.Name, location.WarehouseID)
		logger.LogOperation("删除货位", userID, details)
	}
	return err
}

// GetLocationByID 获取货位详情
func (s *WarehouseService) GetLocationByID(id uint) (*model.Location, error) {
	var location model.Location
	if err := global.DB.First(&location, id).Error; err != nil {
		return nil, err
	}
	return &location, nil
}

// ListLocations 获取货位列表
// 定义一个临时结构体来接收结果
type LocationWithInventory struct {
	model.Location
	CurrentQuantity float64 `gorm:"column:current_quantity" json:"current_quantity"` // 添加当前库存字段, 明确 gorm 列名
}

// ListLocations 获取货位列表 (包含实时库存)
func (s *WarehouseService) ListLocations(req *request.LocationListRequest) ([]LocationWithInventory, int64, error) { // 修改返回值类型
	var locationsWithInventory []LocationWithInventory // 修改结果类型
	var total int64

	// --- Ensure default pagination ---
	if req.Page <= 0 {
		req.Page = 1
	}
	if req.PageSize <= 0 {
		req.PageSize = 10
	}

	// --- Base query for counting (without group/select modifications) ---
	countBaseQuery := global.DB.Model(&model.Location{})

	// --- Query for fetching data ---
	query := global.DB.Model(&model.Location{}).
		Select("locations.*, COALESCE(SUM(i.quantity), 0) as current_quantity"). // 选择库位所有字段 和 库存总和
		Joins("LEFT JOIN inventories i ON i.location_id = locations.id").        // 左连接库存表
		Group("locations.id")                                                    // 按库位ID分组

	// --- Apply Filters to both queries ---
	if req.WarehouseID != nil { // Filter by warehouse if provided
		query = query.Where("locations.warehouse_id = ?", *req.WarehouseID)
		countBaseQuery = countBaseQuery.Where("warehouse_id = ?", *req.WarehouseID)
	}
	if req.Keyword != "" {
		// Apply keyword filter to both queries, ensuring correct table prefix
		keywordCondition := "locations.code LIKE ? OR locations.name LIKE ?"
		keywordArgs := []interface{}{"%" + req.Keyword + "%", "%" + req.Keyword + "%"}
		query = query.Where(keywordCondition, keywordArgs...)
		countBaseQuery = countBaseQuery.Where(keywordCondition, keywordArgs...)
	}
	if req.Type != "" {
		typeCondition := "locations.type = ?"
		query = query.Where(typeCondition, req.Type)
		countBaseQuery = countBaseQuery.Where(typeCondition, req.Type)
	}
	if req.Area != "" {
		areaCondition := "locations.area = ?"
		query = query.Where(areaCondition, req.Area)
		countBaseQuery = countBaseQuery.Where(areaCondition, req.Area)
	}
	if req.Shelf != "" {
		shelfCondition := "locations.shelf = ?"
		query = query.Where(shelfCondition, req.Shelf)
		countBaseQuery = countBaseQuery.Where(shelfCondition, req.Shelf)
	}
	if req.Level != "" {
		levelCondition := "locations.level = ?"
		query = query.Where(levelCondition, req.Level)
		countBaseQuery = countBaseQuery.Where(levelCondition, req.Level)
	}
	if req.Status != nil {
		statusCondition := "locations.status = ?"
		query = query.Where(statusCondition, *req.Status)
		countBaseQuery = countBaseQuery.Where(statusCondition, *req.Status)
	}

	// --- Count total using the filtered base query ---
	if err := countBaseQuery.Count(&total).Error; err != nil {
		logger.Error("Failed to count locations", zap.Error(err))
		return nil, 0, err
	}

	if total == 0 {
		return locationsWithInventory, 0, nil // Return empty slice of correct type
	}

	// --- Fetch list ---
	offset := (req.Page - 1) * req.PageSize
	limit := req.PageSize
	logger.Debug("Fetching locations with inventory and pagination", zap.Int("offset", offset), zap.Int("limit", limit))

	// 使用 Scan 将结果映射到目标结构体, 应用排序
	result := query.Order("locations.created_at desc").Offset(offset).Limit(limit).Scan(&locationsWithInventory)

	if result.Error != nil {
		logger.Error("Failed to find locations", zap.Error(result.Error), zap.Any("request", req))
		// Log the SQL for debugging the Scan query
		sqlFind := query.ToSQL(func(tx *gorm.DB) *gorm.DB {
			return tx.Order("locations.created_at desc").Offset(offset).Limit(limit).Scan(&[]LocationWithInventory{})
		})
		logger.Debug("Failed Find Locations SQL (Scan)", zap.String("sql", sqlFind))
		return nil, 0, result.Error
	}
	logger.Debug("Location with inventory find operation completed", zap.Int64("rowsAffected", result.RowsAffected), zap.Int("listLength", len(locationsWithInventory)))

	return locationsWithInventory, total, nil
}
